Before v0.104.0, code executed at renderer level (e.g. front-end scripts)
could directly require @electron/remote or
even Node.js APIs (e.g. process or fs). While
this makes them powerful, it also exposes a significant attack surface
— any XSS or untrusted script in a note becomes remote code execution on
the host.
To mitigate this, the client-side no longer has access to these APIs and everything must go through the Electron API instead.
The Frontend and Backend APIs reside in a global variable, api,
that can be used anywhere in the script. For example, to display a message
to the user the following front-end script can be used:
api.showMessage("Hello world.");
The Electron API is different: it lives on window.electronApi (exposed
by the desktop preload script), not on api,
and is only available in the Electron desktop build. Frontend scripts that
use it should guard with if (window.electronApi) so
they degrade cleanly in the browser and standalone (WASM) builds.
window.electronApi?.window.setZoomFactor(1.2);
Some of the APIs have additional safe-guards built in which prevent unauthorized access.
The shell group is the most heavily validated:
openExternalis restricted to an allowlisted set of URL schemes
(blocking file:, smb:,
Follina-class schemes, etc.), openPath must
resolve under the Trilium data directory or temporary directory, and
downloadURLis locked to the app's own origin. Invalid input throws
on the main side rather than being silently passed through.